package opdb

import (
	"database/sql/driver"
	"fmt"
	"io"
	"log"
	"time"

	opio "gitee.com/haodreams/godriver/opdb/opio/driver"
)

// Rows is an iterator over an executed query's results.
type Rows struct {
	columns     []string
	numColumn   int
	firstValues []driver.Value
	ds          *opio.OPDataSet
}

func newRows(ds *opio.OPDataSet) (rows *Rows, err error) {
	rows = new(Rows)
	rows.ds = ds

	return rows, rows.init()
}

func (m *Rows) init() (err error) {
	if m.ds == nil {
		return io.EOF
	}

	ok, err := m.ds.Next()
	if err != nil {
		return
	}
	if ok {
		cols := m.ds.GetColumns()
		m.columns = make([]string, len(cols))
		m.numColumn = len(m.columns)
		m.firstValues = make([]driver.Value, m.numColumn)
		for i, col := range cols {
			m.columns[i] = col.GetName()
			m.firstValues[i], err = m.ds.GetValue(uint32(i))
			if err != nil {
				return err
			}
			if t, ok := m.firstValues[i].(time.Time); ok {
				m.firstValues[i] = t.Unix()
			} else if t, ok := m.firstValues[i].(bool); ok {
				if t {
					m.firstValues[i] = 1
				} else {
					m.firstValues[i] = 0
				}
			}
		}
	} else {
		// err = io.EOF
		m.ds.Close()
		m.ds = nil
	}
	return
}

// Columns returns the names of the columns. The number of
// columns of the result is inferred from the length of the
// slice. If a particular column name isn't known, an empty
// string should be returned for that entry.
func (m *Rows) Columns() []string {
	return m.columns
}

// Close closes the rows iterator.
func (m *Rows) Close() error {
	if m.ds != nil {
		m.ds.Close()
		m.ds = nil
	}
	return nil
}

// Next is called to populate the next row of data into
// the provided slice. The provided slice will be the same
// size as the Columns() are wide.
//
// Next should return io.EOF when there are no more rows.
//
// The dest should not be written to outside of Next. Care
// should be taken when closing Rows not to modify
// a buffer held in dest.
func (m *Rows) Next(dest []driver.Value) error {
	if m.ds == nil {
		return io.EOF
	}

	if m.firstValues != nil {
		for i := range dest {
			dest[i] = m.firstValues[i]
		}
		m.firstValues = nil
		return nil
	}

	if ok, err := m.ds.Next(); ok && err == nil {
		n := len(m.columns)
		if len(dest) < n {
			n = len(dest)
		}
		var err error
		//log.Println(m.ds.GetValue(uint32(0)))
		if n == 0 {
			idMsg := ""
			ecMsg := ""
			for i, col := range m.ds.GetColumns() {
				switch col.GetName() {
				case "EC":
					value, _ := m.ds.GetValue(uint32(i))
					if value != 0 {
						if i > 0 {
							ecMsg = fmt.Sprint("EC:", value)
						}
					}
				case "ID":
					value, _ := m.ds.GetValue(uint32(i))
					idMsg = fmt.Sprint("ID:", value)
				}
			}
			if ecMsg != "" {
				log.Println(idMsg, ecMsg)
			}
		} else {
			for i := 0; i < n; i++ {
				dest[i], err = m.ds.GetValue(uint32(i))
				if err != nil {
					return err
				}
				if t, ok := dest[i].(time.Time); ok {
					dest[i] = t.Unix()
				} else if t, ok := dest[i].(bool); ok {
					if t {
						dest[i] = 1
					} else {
						dest[i] = 0
					}
				}
			}
		}

		return nil
	}
	return io.EOF
}
